package top.v5it.japi.plus.support.limit;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;

/**
 * 读取 limit.cnf 文件并解析缓存
 *
 * @author zhanpu
 * @date 2022/8/12
 */
@Slf4j
class JapiLimitCnf {

    private static final String PREFIX = "[";
    private static final String SUFFIX = "]";
    private static final String GLOBAL = "global";
    private static final String MORE = "more";
    private static final String FILENAME = "limit.cnf";
    private static final String ENV_KEY = FILENAME;
    private static Map<String, Map<String, LimitCnf>> limitMap;

    JapiLimitCnf() {
        init();
    }

    /**
     * 初始化
     */
    void init() {
        log.info("初始化limit配置");
        Resource resource = new ClassPathResource(FILENAME);
        List<String> lines = null;
        try {
            if (!resource.exists()) {
                final String filepath = System.getProperty(ENV_KEY);
                resource = new FileUrlResource(filepath);
            }
            lines = FileUtil.readLines(resource.getURL(), Charset.defaultCharset());
        } catch (IOException e) {
            log.warn(e.getMessage(), e);
        }
        if (CollUtil.isEmpty(lines)) return;
        Map<String, Map<String, LimitCnf>> map = new HashMap<>();
        String group = null;
        for (String line : lines) {
            if (StrUtil.isBlank(line)) continue;
            if (StrUtil.startWith(line, '#')) continue;
            line = StrUtil.trim(line);
            if (StrUtil.contains(line, PREFIX)
                    && StrUtil.contains(line, SUFFIX)
                    && map.put(group = line, new HashMap<>()) == null) continue;
            if (StrUtil.isEmpty(group)) throw new UnknownFormatConversionException("[" + FILENAME + "]没有[]分组标识");
            final List<String> rows = StrUtil.splitTrim(line, StrUtil.C_COMMA);
            final int size = rows.size();
            if (size < 4) continue;
            final LimitCnf limitCnf = new LimitCnf()
                    .setKey(rows.get(0))
                    .setLimit(rows.get(1))
                    .setTime(rows.get(2))
                    .setType(rows.get(3));
            Optional.of(limitCnf)
                    .filter(t -> NumberUtil.isNumber(t.getLimit()))
                    .filter(t -> NumberUtil.isNumber(t.getTime()))
                    .filter(t -> Arrays.stream(JapiLimit.Type.values()).anyMatch(v -> v.name().equals(t.getType())))
                    .orElseThrow(() -> new IllegalFormatFlagsException("%s,%d,%d,(GLOBAL|SIGNAL)"));
            map.get(group).put(limitCnf.getKey(), limitCnf);
        }
        limitMap = Optional
                .ofNullable(limitMap)
                .orElse(MapUtil.unmodifiable(map));
        log.info("完成limit配置初始化");
    }

    /**
     * 填充key
     *
     * @param key
     * @return
     */
    private String fillKey(String key) {
        return (StrUtil.startWith(key, PREFIX)
                && StrUtil.endWith(key, SUFFIX)) ? key : PREFIX + key + SUFFIX;
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    Optional<LimitCnf> get(String key, String path) {
        if (StrUtil.isBlank(path)) return Optional.empty();
        final String fillKey = fillKey(key);
        return Optional
                .ofNullable(limitMap)
                .map(t -> t.get(fillKey))
                .map(t -> t.get(path));
    }

    /**
     * 获取对象
     *
     * @param path
     * @return
     */
    Optional<LimitCnf> get(String path) {
        return get(GLOBAL, path);
    }

    /**
     * 获取对象
     *
     * @param path
     * @param global
     * @return
     */
    Optional<LimitCnf> get(String path, boolean global) {
        return global ? get(path) : get(MORE, path);
    }

}
